package com.qfedu.edu.seckill.classservice.service.impl;

import com.qfedu.edu.seckill.classservice.feign.api.StockStorageService;
import com.qfedu.edu.seckill.classservice.utils.AliOssCloudUtils;
import com.qfedu.edu.seckill.classservice.utils.FTPUtil;
import com.qfedu.edu.seckill.constance.FtpConstance;
import com.qfedu.edu.seckill.constance.RedisConstance;
import com.qfedu.edu.seckill.exception.BusinessException;
import com.qfedu.edu.seckill.manager.RedisManager;
import com.qfedu.edu.seckill.po.Class;
import com.qfedu.edu.seckill.classservice.mapper.ClassMapper;
import com.qfedu.edu.seckill.classservice.service.ClassService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qfedu.edu.seckill.result.R;
import com.qfedu.edu.seckill.result.ResponseCode;
import com.qfedu.edu.seckill.utils.FileNameUtils;
import com.qfedu.edu.seckill.vo.AddClassVo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author xiaobobo
 * @since 2025-01-13
 */
@Service
@Slf4j
public class ClassServiceImpl extends ServiceImpl<ClassMapper, Class> implements ClassService {

    @Autowired
    private ClassMapper classMapper;

    @Autowired
    private RedisManager redisManager;

    @Autowired
    private StockStorageService stockStorageService;

    @GlobalTransactional
    @Override
    public void addClass(AddClassVo addClassVo, MultipartFile file) throws Exception {
        //在这里需要做这个数据的校验
        checkParams(addClassVo, file);
        //上传图片到阿里云的OSS去
        String photoPath=uploadFileToOSS(file);
        //接下来将这个图片的地址设置给这个Vo
        addClassVo.setPhoto(photoPath);
        //接下来添加商品到数据库
        addClassToDb(addClassVo);
        //接下来添加库存信息到数据库
        int realQuantity=addStockToDb(addClassVo);
        //生成详情页
        createHTML(addClassVo,realQuantity);
    }

    /**
     * 这个方法的主要作用就是生成详情页(这里会借助于Freemarker模板引擎来生成这个页面)
     * @param addClassVo
     */
    private void createHTML(AddClassVo addClassVo,int realQuantity) throws Exception {
        //先将页面要显示的的数据放到Map中去
        //将我们传递过来的数据 放到一个hashmap中
        Map<String,Object> objectMap=new HashMap<>();
        //接下来向里面放数据
        objectMap.put("className",addClassVo.getClassName());
        objectMap.put("photo",addClassVo.getPhoto());
        objectMap.put("classDes",addClassVo.getClassDesc());
        objectMap.put("classId",addClassVo.getClassId());
        objectMap.put("price",addClassVo.getPrice());
        objectMap.put("seckillPrice",addClassVo.getSecKillPrice());
        objectMap.put("quanty",realQuantity);
        //指定这个模板的路径
        String templatePath="E:/上课内容/class-detail-template/";
        //判断课程id有没有数据
        if(StringUtils.isEmpty(addClassVo.getClassId())){
            throw new BusinessException(ResponseCode.CLASS_ID_NOT_FOUND_EXCEPTION);
        }
        //执行到这里  说明参数是没有问题的
        String htmlPath=generateHTML(templatePath,"class_detail.ftl",objectMap,addClassVo.getClassId()+".html",templatePath);
        log.info("HTML文件生成完成:{}",htmlPath);
        uploadHTMLToOpenresty(htmlPath);
        log.info("上传文件到阿里云的OSS成功:{}",htmlPath);
    }

    /**
     *
     * @param htmlPath：这个是咋们的HTML文件的全路径
     */
    private void uploadHTMLToOpenresty(String htmlPath) {
        //通过HTML全路径去上传咋们的这个文件到Linux服务器上(FTP服务器)
        FTPClient ftpClient = new FTPUtil().getFTPClient(FtpConstance.getFtpHost(),
                FtpConstance.getFtpPort(),
                FtpConstance.getFtpUser(),
                FtpConstance.getFtpPassword());
        //接下来上传文件
        FTPUtil.uploadFile(ftpClient,htmlPath,FtpConstance.getFtpUploadPath());
        log.info("上传文件到Openresty是成功的....");
    }

    /**
     *
     * @param templatePath：模板所有的路径
     * @param templateName：模板的名字
     * @param objectMap：模板需要的数据
     * @param desFileName：html文件的名字
     * @param htmlPath：HTML文件保存的路径
     * @return
     */
    private String generateHTML(String templatePath, String templateName, Map<String, Object> objectMap, String desFileName, String htmlPath) throws Exception {
        //第一步：通过模板的路径和模板的名字 找到这个模板
        Template template= getTemplateConfiguration(templatePath).getTemplate(templateName);
        //设置编码
        template.setEncoding("UTF-8");
        //创建目录
        createDir(htmlPath);
        //找到最终生成了HTML文件所在的这个位置
        File htmlFile = new File(htmlPath + desFileName);
        //第五步：给这个文件添加输出流
        FileOutputStream fileOutputStream = new FileOutputStream(htmlFile);
        //第六步：最终的目的是要将模板中的内容输出到 上面的文件中
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream,"UTF-8"));
        //输出内容到文件
        template.process(objectMap,bufferedWriter);
        bufferedWriter.close();
        fileOutputStream.close();
        log.info("HTML页面生成成功....");
        return htmlPath + desFileName;
    }

    /**
     * 这里实际上是判断这个保存HTML文件的这个文件夹是否存在 如果不存在的话 那么
     * 直接创建这个文件夹
     * @param htmlPath
     */
    private void createDir(String htmlPath) throws IOException {
        File file = new File(htmlPath);
        if(!file.exists()){
            file.createNewFile();
        }
    }

    /**
     * 通过模板的路径找到这个模板的配置对象
     * @param templatePath
     * @return
     */
    private Configuration getTemplateConfiguration(String templatePath) {
        Configuration configuration = new Configuration();
        //设置了配置的编码
        configuration.setEncoding(Locale.CHINA,"UTF-8");
        configuration.setClassForTemplateLoading(this.getClass(),
                "/websiteroot/freemarker");
        try {
            configuration.setDirectoryForTemplateLoading(new File(
                    templatePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return configuration;
    }

    /**
     * 添加库存信息到数据库(跨库调用的问题)
     * @param addClassVo
     */
    private int addStockToDb(AddClassVo addClassVo) {
        R r=stockStorageService.addStockStorage(
                addClassVo.getClassId(),
                addClassVo.getCount());
        return (int) r.getDataMap().get("quantity");
    }

    /**
     * 添加商品到数据库
     * @param addClassVo
     */
    private void addClassToDb(AddClassVo addClassVo) {
        //首先第一步：将Vo转换成PO
        //将VO转换成PO对象
        Class aClass = new Class();
        //那就改成最普通的玩法
        aClass.setClassDesc(addClassVo.getClassDesc());
        aClass.setClassName(addClassVo.getClassName());
        aClass.setModel(addClassVo.getModel());
        aClass.setPrice(addClassVo.getPrice());
        aClass.setSecKillPrice(addClassVo.getSecKillPrice());
        aClass.setStartTime(addClassVo.getStartTime());
        aClass.setEndTime(addClassVo.getEndTime());
        aClass.setPhoto(addClassVo.getPhoto());
        //向数据库添加数据
        classMapper.insert(aClass);
        //所以此时向Vo中添加classId方便后续操作
        addClassVo.setClassId(aClass.getClassId());
        //所以添加库存信息到Redis中
        addStockToRedis(addClassVo);
    }

    /**
     * 添加库存信息到Redis中
     * @param addClassVo
     */
    private void addStockToRedis(AddClassVo addClassVo) {
        //这个库存信息：怎么保存选择什么数据类型呢?
        // String类型  key：pro:stock:商品id   商品数量
        //首先构建库存的这个key
        String stockKey= RedisConstance.getProStockPrefix()+addClassVo.getClassId();
        log.info("商品的库存key是:{}",stockKey);
        //接下来获取商品的数量
        Integer countVal = addClassVo.getCount();
        //存在一个问题了... 啥时候过期呢?  秒杀的结束时间-开始时间
        //Date endTime = addClassVo.getEndTime();
        redisManager.addObject(stockKey,countVal,30, TimeUnit.DAYS);
        log.info("向Redis中添加库存信息成功....");
    }

    /**
     * 上传图片到阿里云的OSS去
     * @param file
     * @return
     */
    private String uploadFileToOSS(MultipartFile file) {
        //首先在这里生成图片的名字
        //分布式锁...
        String fileName = FileNameUtils.getFileName(".png");
        try {
            String photoPath = new AliOssCloudUtils().uploadFile2OSS(file.getInputStream(), fileName);
            log.info("上传到OSS的图片地址是:"+photoPath);
            return photoPath;
        } catch (IOException e) {
            throw new BusinessException(ResponseCode.PHOTO_UPLOAD_EXCEPTION);
        }
    }

    /**
     * 校验数据的合法性
     *
     * @param addClassVo
     * @param file
     */
    private void checkParams(AddClassVo addClassVo, MultipartFile file) {
        if (null == addClassVo || StringUtils.isEmpty(addClassVo.getClassName()) || null == addClassVo.getPrice() || null == addClassVo.getSecKillPrice() || null == addClassVo.getCount() || null == addClassVo.getStartTime() || null == addClassVo.getEndTime() || null == addClassVo.getModel() || null == addClassVo.getHorse() || null == file) {
            throw new BusinessException(ResponseCode.PARAM_EXCEPTION);
        }
        log.info("参数校验完成....");
    }

}
